package com.wondertek.onvif.service.connection;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 海康设备专用连接管理器
 * 专门处理海康设备的"Unexpected end of file"等连接问题
 */
@Slf4j
@Component
public class HikvisionConnectionManager {
    
    private static final int CONNECTION_TIMEOUT = 5000; // 5秒连接超时
    private static final int READ_TIMEOUT = 5000; // 5秒读取超时
    private static final int SOCKET_TIMEOUT = 5000; // 5秒Socket超时
    private static final int MAX_RETRIES = 3;
    private static final long BASE_RETRY_DELAY = 1000; // 1秒基础重试延迟
    
    private final AtomicInteger connectionCounter = new AtomicInteger(0);
    
    /**
     * 发送SOAP请求到海康设备
     */
    public String sendSoapRequest(String serviceUrl, String soapRequest, String authHeader) throws IOException {
        int connectionId = connectionCounter.incrementAndGet();
        log.info("[连接{}] 开始海康设备SOAP请求: {}", connectionId, serviceUrl);
        
        IOException lastException = null;
        
        for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            long attemptStartTime = System.currentTimeMillis();
            log.info("[连接{}] 第{}/{}次尝试开始", connectionId, attempt, MAX_RETRIES);
            
            try {
                // 首先测试基础连接
                if (!testBasicConnectivity(serviceUrl, connectionId)) {
                    throw new IOException("基础连接测试失败");
                }
                
                // 发送SOAP请求
                String response = performSoapRequest(serviceUrl, soapRequest, authHeader, connectionId, attempt);
                
                long attemptDuration = System.currentTimeMillis() - attemptStartTime;
                log.info("[连接{}] 请求成功，耗时: {}ms，响应长度: {}", 
                    connectionId, attemptDuration, response != null ? response.length() : 0);
                
                return response;
                
            } catch (java.net.SocketException e) {
                lastException = e;
                long attemptDuration = System.currentTimeMillis() - attemptStartTime;
                
                if (e.getMessage() != null && e.getMessage().contains("Unexpected end of file")) {
                    log.warn("[连接{}] 第{}次尝试遇到连接中断，耗时: {}ms - {}", 
                        connectionId, attempt, attemptDuration, e.getMessage());
                    
                    // 清理系统连接状态
                    cleanupConnectionState();
                    
                    if (attempt < MAX_RETRIES) {
                        long delay = calculateRetryDelay(attempt);
                        log.info("[连接{}] 等待{}ms后重试", connectionId, delay);
                        waitForRetry(delay);
                        continue;
                    }
                } else {
                    log.error("[连接{}] 第{}次尝试Socket异常: {}", connectionId, attempt, e.getMessage());
                }
            } catch (java.io.IOException e) {
                lastException = e;
                long attemptDuration = System.currentTimeMillis() - attemptStartTime;
                
                if (isRetryableIOException(e)) {
                    log.warn("[连接{}] 第{}次尝试IO异常(可重试)，耗时: {}ms - {}", 
                        connectionId, attempt, attemptDuration, e.getMessage());
                    
                    if (attempt < MAX_RETRIES) {
                        long delay = calculateRetryDelay(attempt);
                        log.info("[连接{}] 等待{}ms后重试", connectionId, delay);
                        waitForRetry(delay);
                        continue;
                    }
                } else {
                    log.error("[连接{}] 第{}次尝试IO异常(不可重试): {}", connectionId, attempt, e.getMessage());
                    throw e;
                }
            } catch (Exception e) {
                lastException = new IOException("请求异常: " + e.getMessage(), e);
                log.error("[连接{}] 第{}次尝试异常: {}", connectionId, attempt, e.getMessage());
                
                if (attempt == MAX_RETRIES) {
                    break;
                }
            }
        }
        
        log.error("[连接{}] 所有重试均失败", connectionId);
        throw new IOException("海康设备连接失败，已重试" + MAX_RETRIES + "次: " + serviceUrl, lastException);
    }
    
    /**
     * 测试基础连接性
     */
    private boolean testBasicConnectivity(String serviceUrl, int connectionId) {
        try {
            URL url = new URL(serviceUrl);
            String host = url.getHost();
            int port = url.getPort() == -1 ? ("https".equals(url.getProtocol()) ? 443 : 80) : url.getPort();
            
            log.debug("[连接{}] 测试基础连接: {}:{}", connectionId, host, port);
            
            try (Socket socket = new Socket()) {
                socket.setSoTimeout(5000); // 5秒超时
                socket.connect(new InetSocketAddress(host, port), 5000);
                log.debug("[连接{}] 基础连接测试成功", connectionId);
                return true;
            }
        } catch (Exception e) {
            log.warn("[连接{}] 基础连接测试失败: {}", connectionId, e.getMessage());
            return false;
        }
    }
    
    /**
     * 执行SOAP请求
     */
    private String performSoapRequest(String serviceUrl, String soapRequest, String authHeader, int connectionId, int attempt) throws IOException {
        HttpURLConnection connection = null;
        
        try {
            URL url = new URL(serviceUrl);
            connection = (HttpURLConnection) url.openConnection();
            
            // 配置连接参数
            setupConnection(connection, authHeader, connectionId);
            
            // 发送请求
            sendRequest(connection, soapRequest, connectionId);
            
            // 读取响应
            return readResponse(connection, connectionId);
            
        } finally {
            if (connection != null) {
                safeDisconnect(connection, connectionId);
            }
        }
    }
    
    /**
     * 配置HTTP连接
     */
    private void setupConnection(HttpURLConnection connection, String authHeader, int connectionId) throws IOException {
        connection.setRequestMethod("POST");
        connection.setConnectTimeout(CONNECTION_TIMEOUT);
        connection.setReadTimeout(READ_TIMEOUT);
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setInstanceFollowRedirects(false);
        
        // 设置请求头
        connection.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
        connection.setRequestProperty("SOAPAction", "");
        connection.setRequestProperty("User-Agent", "ONVIF-Manager/1.0 (Hikvision-Optimized)");
        connection.setRequestProperty("Accept", "*/*");
        connection.setRequestProperty("Connection", "close"); // 强制关闭连接
        connection.setRequestProperty("Cache-Control", "no-cache");
        connection.setRequestProperty("Pragma", "no-cache");
        connection.setRequestProperty("Accept-Encoding", "identity"); // 禁用压缩
        
        if (StrUtil.isNotEmpty(authHeader)) {
            connection.setRequestProperty("Authorization", authHeader);
        }
        
        log.debug("[连接{}] HTTP连接配置完成", connectionId);
    }
    
    /**
     * 发送请求数据
     */
    private void sendRequest(HttpURLConnection connection, String soapRequest, int connectionId) throws IOException {
        byte[] requestData = soapRequest.getBytes(StandardCharsets.UTF_8);
        connection.setRequestProperty("Content-Length", String.valueOf(requestData.length));
        
        log.debug("[连接{}] 发送请求数据，长度: {}", connectionId, requestData.length);
        
        try (OutputStream os = connection.getOutputStream()) {
            os.write(requestData);
            os.flush();
        }
        
        log.debug("[连接{}] 请求数据发送完成", connectionId);
    }
    
    /**
     * 读取响应数据
     */
    private String readResponse(HttpURLConnection connection, int connectionId) throws IOException {
        int responseCode = connection.getResponseCode();
        log.debug("[连接{}] 响应码: {}", connectionId, responseCode);
        
        InputStream inputStream = null;
        try {
            if (responseCode >= 200 && responseCode < 300) {
                inputStream = connection.getInputStream();
            } else {
                inputStream = connection.getErrorStream();
                if (inputStream == null) {
                    throw new IOException("HTTP错误: " + responseCode + " " + connection.getResponseMessage());
                }
            }
            
            String response = readInputStream(inputStream);
            log.debug("[连接{}] 响应读取完成，长度: {}", connectionId, response.length());
            
            if (responseCode >= 200 && responseCode < 300) {
                return response;
            } else {
                throw new IOException("HTTP错误 " + responseCode + ": " + response);
            }
            
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.debug("[连接{}] 关闭输入流异常: {}", connectionId, e.getMessage());
                }
            }
        }
    }
    
    /**
     * 读取输入流
     */
    private String readInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] data = new byte[4096];
        int bytesRead;
        
        while ((bytesRead = inputStream.read(data)) != -1) {
            buffer.write(data, 0, bytesRead);
        }
        
        return buffer.toString(StandardCharsets.UTF_8.name());
    }
    
    /**
     * 安全断开连接
     */
    private void safeDisconnect(HttpURLConnection connection, int connectionId) {
        try {
            // 确保所有流都被正确关闭
            try {
                if (connection.getInputStream() != null) {
                    connection.getInputStream().close();
                }
            } catch (Exception e) {
                // 忽略
            }
            
            try {
                if (connection.getErrorStream() != null) {
                    connection.getErrorStream().close();
                }
            } catch (Exception e) {
                // 忽略
            }
            
            connection.disconnect();
            log.debug("[连接{}] 连接已安全断开", connectionId);
            
        } catch (Exception e) {
            log.debug("[连接{}] 断开连接异常: {}", connectionId, e.getMessage());
        }
    }
    
    /**
     * 清理连接状态
     */
    private void cleanupConnectionState() {
        System.clearProperty("http.keepAlive");
        System.clearProperty("http.maxConnections");
        System.setProperty("http.keepAlive", "false");
        System.setProperty("http.maxConnections", "1");
        log.debug("连接状态已清理");
    }
    
    /**
     * 计算重试延迟
     */
    private long calculateRetryDelay(int attempt) {
        // 线性增长延迟，避免指数增长
        return Math.min(BASE_RETRY_DELAY * attempt, 3000); // 最大3秒
    }
    
    /**
     * 等待重试
     */
    private void waitForRetry(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("重试等待被中断", e);
        }
    }
    
    /**
     * 判断是否为可重试的IO异常
     */
    private boolean isRetryableIOException(IOException e) {
        if (e.getMessage() == null) {
            return false;
        }
        
        String message = e.getMessage().toLowerCase();
        return message.contains("connection reset") ||
               message.contains("connection timed out") ||
               message.contains("read timed out") ||
               message.contains("connect timed out") ||
               message.contains("socket closed") ||
               message.contains("broken pipe");
    }
}